﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using Basmus.DataStructures;
using System.Linq.Expressions;
using Basmus.DataStructures.Generic;
using System.Reflection;

namespace Basmus.BindingFramework
{
	public class LinqToSqlClasses : ITupleStringIdentity
	{
		public DataContext DataContext{get;set;}

		public LinqToSqlClasses(DataContext dataContext)
		{
			DataContext = dataContext;
		}

		private object[] identityObjects(object entity, IEnumerable<PropertyInfo> properties)
		{
			return properties.Select(x => x.GetValue(entity, null)).ToArray();
		}


		#region ITupleStringIdentity Members

		public IEnumerable<Type> AvailableTypes { get; private set; }

		public string Identity(Tuple tuple)
		{
			var types = DataContext.Mapping.GetMetaType(tuple.ElementType).IdentityMembers.Select(x => x.Type).ToArray();
			var properties = DataContext.Mapping.GetMetaType(tuple.ElementType).IdentityMembers.Select(x => x.Member).Where(x => x.MemberType == MemberTypes.Property).Cast<PropertyInfo>();
			if (tuple.IsEnumerable)
			{
				var t = tuple.Entity.AsEnumerable<object>().Select(x => identityObjects(x, properties));
				var identityObj = new Tuple<object[]>(t);
				var identityString = identityObj.ToIdentityKeysTuple().ToIdentityTuple("!").ToIdentityString(",");
				return identityString;
			}
			else
			{
				var t = identityObjects(tuple.Entity, properties);
				var identityObj = new Tuple<object[]>(t);
				var identityString = identityObj.ToIdentityKeysTuple().ToIdentityTuple("!").ToIdentityString(",");
				return identityString;
			}
		}
		public Tuple Tuple(Type elementType, string identity)
		{
			var types = DataContext.Mapping.GetMetaType(elementType).IdentityMembers.Select(x => x.Type).ToArray();
			var properties = DataContext.Mapping.GetMetaType(elementType).IdentityMembers.Select(x => x.Member).Where(x => x.MemberType == MemberTypes.Property).Cast<PropertyInfo>().ToArray();
			var identityObjectTuple = identity.ToIdentityTuple(",").ToIdentityKeysTuple("!").ToIdentityObjectsTuple(types);
			if (identityObjectTuple.IsEnumerable)
			{
				var entity = Expression.Parameter(elementType, "entity");
				var propertyContdition = entity.PropertyConditionExpression(properties, identityObjectTuple.Elements);
				var lambdaEx = Expression.Lambda(propertyContdition, entity);
				var dataContextTableEx = DataContext.GetTable(elementType).Expression;
				var callEx = Expression.Call(typeof(Queryable), "Where", new Type[1] { elementType }, dataContextTableEx, lambdaEx);
				var action = Expression.Lambda(callEx).Compile();
				var enitities = action.DynamicInvoke();
				return new Tuple(elementType, enitities, true);
			}
			else
			{
				var entity = Expression.Parameter(elementType, "entity");
				var propertyContdition = entity.PropertyConditionExpression(properties, identityObjectTuple.Element);
				var lambdaEx = Expression.Lambda(propertyContdition, entity);
				var dataContextTableEx = DataContext.GetTable(elementType).Expression;
				var callEx = Expression.Call(typeof(Queryable), "Single", new Type[1] { elementType }, dataContextTableEx, lambdaEx);
				var action = Expression.Lambda(callEx).Compile();
				var enitity = action.DynamicInvoke();
				return new Tuple(elementType, enitity, false);
			}
		}

		#endregion
	}
}
